// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#include "StandinActor.h"
#include "Materials/Material.h"
#if WITH_EDITOR
#include "SimplygonAssetUserData.h"
#include "Engine/StaticMeshActor.h"
#endif
#include "Engine/StaticMesh.h"
#include "Algo/Accumulate.h"
#include "Serialization/ObjectWriter.h"

DEFINE_LOG_CATEGORY_STATIC(LogStandinMeshActor, Verbose, All);
#define LOCTEXT_NAMESPACE "StandInMeshActor"



// Change this to force all standins to be rebuilt
#define STANDIN_BUILD_KEY_VER TEXT("2CF0EDC0F4444058A890C50C5B20AEDA")

/**
 * @brief PipelineWriter for binary serialization to memory for hash usage
*/
class FPipelineWriter : public FObjectWriter
{
public:
	FPipelineWriter(UObject* InObj, TArray<uint8>& InBytes)
		:FObjectWriter(InBytes)
	{
		SetIsLoading(false);
		SetIsSaving(true);
		SetIsPersistent(false);
		SetWantBinaryPropertySerialization(true);

		InObj->SerializeScriptProperties(*this);
	}

	/**
	 * Any specific properties can be skipped that we do not want here. 
	*/
	bool ShouldSkipProperty(const FProperty* InProperty) const
	{
		bool bSkip = false;

		if (InProperty->HasAnyPropertyFlags(CPF_Transient))
		{
			bSkip = true;
		}

		return bSkip;
	}
	/**
	 * Note ObjectWriter FNameSerailzier add in extra information. We just want the name for hash generation.
	*/
	virtual FArchive& operator<<(FName& N) override
	{
		if (IsSaving())
		{
			FString StringName = N.ToString();
			*this << StringName;
		}
		return *this;
	}

	virtual FArchive& operator<<(UObject*& Res) override
	{
		if(Res)
			Res->SerializeScriptProperties(*this);
		return *this;
	}
};


static SIZE_T GetMemoryUsedByStaticMesh(UStaticMesh* InMesh)
{
	SIZE_T TotalMemoryUsed = 0;
	if (InMesh)
	{
		TArray<UMaterialInterface*> UniqueMaterials;
		for (auto& Material : InMesh->GetStaticMaterials())
		{
			if (Material.MaterialInterface != nullptr)
			{
				UniqueMaterials.AddUnique(Material.MaterialInterface);
			}
		}

		TArray<UTexture*> UniqueTextures;
		for (auto& Material : UniqueMaterials)
		{
			TArray<UTexture*> OutLocalTextures;
			Material->GetUsedTextures(OutLocalTextures, EMaterialQualityLevel::High, false, ERHIFeatureLevel::SM5, false);
			for (auto& Texture : OutLocalTextures)
			{
				UniqueTextures.AddUnique(Texture);
			}
		}
		TotalMemoryUsed += InMesh->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal);
		TotalMemoryUsed += Algo::Accumulate<SIZE_T>(UniqueMaterials, 0, [](SIZE_T /*Total*/, UMaterialInterface* Val) {return Val->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal); });
		TotalMemoryUsed += Algo::Accumulate<SIZE_T>(UniqueTextures, 0, [](SIZE_T /*Total*/, UTexture* Val) {return Val->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal); });
	}
	return TotalMemoryUsed;
}

void RecursivelyGetStaticMeshComponents(const AActor* InActor, TInlineComponentArray<UStaticMeshComponent*>& OutComponents)
{
	if (InActor == nullptr)
	{
		return;
	}

	// Loop over all the components and build array
	TArray<UActorComponent*> ActorComponents;
	InActor->GetComponents(ActorComponents);
	for (UActorComponent* ActorComponent : ActorComponents)
	{
		UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(ActorComponent);
		UChildActorComponent* ChildActorComponent = Cast<UChildActorComponent>(ActorComponent);
		if (StaticMeshComponent && StaticMeshComponent->GetStaticMesh() && !StaticMeshComponent->bHiddenInGame)
		{
			OutComponents.Add(StaticMeshComponent);
		}
		if (ChildActorComponent && ChildActorComponent->GetChildActor())
		{
			AActor* ChildActor = ChildActorComponent->GetChildActor();
			//Recursively add all static mesh components from child actors
			RecursivelyGetStaticMeshComponents(ChildActor, OutComponents);
		}
	}
}

void SetComponentIsEditorOnly(const AActor* InActor, bool IsEditorOnly)
{
	TInlineComponentArray<UStaticMeshComponent*> StaticMeshComponents;
	RecursivelyGetStaticMeshComponents(InActor, StaticMeshComponents);
	for (UStaticMeshComponent* Component : StaticMeshComponents)
	{
		Component->bIsEditorOnly = IsEditorOnly;
	}
}

AStandInMeshActor::AStandInMeshActor(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	SetCanBeDamaged(false);
	// Cast shadows if any sub-actors do
	bool bCastsShadow = false;
	bool bCastsStaticShadow = false;
	bool bCastsDynamicShadow = false;

#if WITH_EDITORONLY_DATA
	bListedInSceneOutliner = true;
#endif

	/*USceneComponent* GroupComponent =*/ CreateDefaultSubobject<USceneComponent>(TEXT("GroupComponent"));
	StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComponent0"));
	//StaticMeshComponent->SetCollisionProfileName(UCollisionProfile::NoCollision_ProfileName);
	StaticMeshComponent->Mobility = EComponentMobility::Static;
	StaticMeshComponent->SetGenerateOverlapEvents(false);
	StaticMeshComponent->CastShadow = bCastsShadow;
	StaticMeshComponent->bCastStaticShadow = bCastsStaticShadow;
	StaticMeshComponent->bCastDynamicShadow = bCastsDynamicShadow;
	StaticMeshComponent->bAllowCullDistanceVolume = false;

	RootComponent = StaticMeshComponent;

	//Pipeline = CreateDefaultSubobject<UStandinNearPipeline>(TEXT("DefaultStandinPipeline0"));

#if WITH_EDITORONLY_DATA
	Id = FGuid::NewGuid();

	bIsGameStandin = false;

	BuildKey = NAME_None; 
	BuildKeyLastUpdateTime = 0.0;
	bIsBuildKeyDirty = false;

	FString DefaultPath = FPaths::ProjectContentDir() + TEXT("Simplygon/Standins");
	PackagePath = FPackageName::FilenameToLongPackageName(DefaultPath + TEXT("/") + GetName());

	TriangleCount = 0;
	SubActorsTriangleCount = 0;

	PipelineHash = 0;
#endif // WITH_EDITORONLY_DATA
}

/** BEGIN [UObject] Interface */
FString AStandInMeshActor::GetDetailedInfoInternal() const
{
	return StaticMeshComponent ? StaticMeshComponent->GetDetailedInfoInternal() : TEXT("No_StaticMeshComponent");
}

bool AStandInMeshActor::NeedsLoadForClient() const
{
	return Super::NeedsLoadForClient();
	//2019-08-09 - Fix for hidden in-game StandinActor
	//return false;
}

void AStandInMeshActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
}

void AStandInMeshActor::PostLoad()
{
#if WITH_EDITOR
	CleanupInvalidSourceActors();
	UpdateMemoryInfo();
#endif
	Super::PostLoad();

}

#if WITH_EDITOR
bool AStandInMeshActor::Modify(bool bAlwaysMarkDirty /* = true */)
{
	bool bSavedToTransactionBuffer = Super::Modify(bAlwaysMarkDirty);
	for (int32 ActorIndex = 0; ActorIndex < SubActors.Num(); ++ActorIndex)
	{
		if (SubActors[ActorIndex] != NULL)
		{
			bSavedToTransactionBuffer = SubActors[ActorIndex]->Modify(bAlwaysMarkDirty) || bSavedToTransactionBuffer;
		}
	}
	return bSavedToTransactionBuffer;
}

void AStandInMeshActor::PreEditChange(FProperty* PropertyThatWillChange)
{
	Super::PreEditChange(PropertyThatWillChange);

	if (PropertyThatWillChange)
	{
		const FName PropertyName = PropertyThatWillChange->GetFName();

		// If the Sub Objects array is changed, in case of asset deletion make sure me flag as dirty since the cluster will be invalid
		if (PropertyName == TEXT("SubObjects"))
		{
			SetIsDirty(true);
		}
	}

	// Flush all pending rendering commands.
	//FlushRenderingCommands();
}

void AStandInMeshActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
	const FName PropertyName = (PropertyChangedEvent.Property != nullptr) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
	
	// Update pipeline hash
	if (PropertyName == GET_MEMBER_NAME_CHECKED(AStandInMeshActor, Pipeline))
	{
		PipelineHash = GeneratePipelineHash();
	}

	Super::PostEditChangeProperty(PropertyChangedEvent);
}
#endif
/** END [UObject] Interface  */

/** BEGIN [AActor] Interface  */
#if WITH_EDITOR
bool AStandInMeshActor::GetReferencedContentObjects(TArray<UObject*>& Objects) const
{
	Super::GetReferencedContentObjects(Objects);
	//Objects.Append(SubObjects);

	//// Retrieve referenced objects for sub actors as well
	for (AActor* SubActor : SubActors)
	{
		SubActor->GetReferencedContentObjects(Objects);
	}
	return true;
}
#endif

FBox AStandInMeshActor::GetComponentsBoundingBox(bool bNonColliding, bool bIncludeFromChildActors) const
{
	FBox BoundBox = Super::GetComponentsBoundingBox(bNonColliding, bIncludeFromChildActors);

	// If BoundBox ends up to nothing create a new invalid one
	if (BoundBox.GetVolume() == 0.0f)
	{
		BoundBox = FBox(EForceInit::ForceInitToZero);
	}

	if (bNonColliding)
	{
		if (StaticMeshComponent && StaticMeshComponent->GetStaticMesh())
		{
			FBoxSphereBounds StaticBound = StaticMeshComponent->GetStaticMesh()->GetBounds();
			FBox StaticBoundBox(BoundBox.GetCenter() - StaticBound.BoxExtent, BoundBox.GetCenter() + StaticBound.BoxExtent);
			BoundBox += StaticBoundBox;
		}
#if WITH_EDITORONLY_DATA
		else
		{
			for (AActor* Actor : SubActors)
			{
				if (Actor)
				{
					BoundBox += Actor->GetComponentsBoundingBox(bNonColliding, bIncludeFromChildActors);
				}
			}
		}
#endif
	}

	return BoundBox;
}

void AStandInMeshActor::PostRegisterAllComponents()
{
	Super::PostRegisterAllComponents();

#if WITH_EDITOR
	// Clean up sub actor if assets were delete manually
	CleanupInvalidSourceActors();

	// Clean up sub objects if assets were delete manually
	CleanSubObjectsArray();

#endif
}
/** END [AActor] Interface  */

#if WITH_EDITOR
void AStandInMeshActor::AddSubActor(AActor* InActor)
{
	SubActors.Add(InActor);

	SetIsDirty(true);
	UpdateMemoryInfo();
	// Reset the shadowing flags and determine them according to our current sub actors
	DetermineShadowingFlags();
}

const bool AStandInMeshActor::RemoveSubActor(AActor* InActor)
{
	if ((InActor != nullptr) && SubActors.Contains(InActor))
	{
		SubActors.Remove(InActor);
		InActor->Modify();
		SetIsDirty(true);

		SetComponentIsEditorOnly(InActor, false);
		HiddenInGame(InActor, false);
		HiddenInEditor(InActor, false);
				
		if (StaticMeshComponent)
		{
			StaticMeshComponent->MarkRenderStateDirty();
		}

		InActor->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);

		// In case the user removes an actor while the HLOD system is force viewing one LOD level
		InActor->SetIsTemporarilyHiddenInEditor(false);

		// Reset the shadowing flags and determine them according to our current sub actors
		DetermineShadowingFlags();
		UpdateMemoryInfo();
		return true;
	}

	return false;
}

void AStandInMeshActor::DetermineShadowingFlags()
{
	// Cast shadows if any sub-actors do
	bool bCastsShadow = false;
	bool bCastsStaticShadow = false;
	bool bCastsDynamicShadow = false;
	for (AActor* Actor : SubActors)
	{
		TArray<UStaticMeshComponent*> StaticMeshComponents;
		Actor->GetComponents<UStaticMeshComponent>(StaticMeshComponents);
		for (UStaticMeshComponent* Component : StaticMeshComponents)
		{
			bCastsShadow |= Component->CastShadow;
			bCastsStaticShadow |= Component->bCastStaticShadow;
			bCastsDynamicShadow |= Component->bCastDynamicShadow;
		}
	}

	StaticMeshComponent->CastShadow = bCastsShadow;
	StaticMeshComponent->bCastStaticShadow = bCastsStaticShadow;
	StaticMeshComponent->bCastDynamicShadow = bCastsDynamicShadow;
	StaticMeshComponent->MarkRenderStateDirty();
}

const bool AStandInMeshActor::IsDirty()
{ 
	auto HasActorChanged = [this]()
	{
		FName Key = GenerateBuildKey();
		const FName& CurrentKey = GetBuildKey();
		return CurrentKey != Key;
	};

	const double CurrentTime = FPlatformTime::Seconds();
	if (CurrentTime - BuildKeyLastUpdateTime > 0.5)
	{
		bIsBuildKeyDirty = HasActorChanged();
		BuildKeyLastUpdateTime = CurrentTime;
	}

	return bDirty || bIsBuildKeyDirty;
}

void AStandInMeshActor::SetIsDirty(const bool bNewState)
{
	bDirty = bNewState;

	// Set parent ReplacementActor dirty as well if bNewState = true
	if (IsDirty())
	{
		// If this ReplacementActor is a SubActor at a higher LOD level mark parent dirty as well
		UPrimitiveComponent* LODParentComponent = StaticMeshComponent->GetLODParentPrimitive();
		if (LODParentComponent)
		{
			AStandInMeshActor* LODParentActor = Cast<AStandInMeshActor>(LODParentComponent->GetOwner());
			if (LODParentActor)
			{
				LODParentActor->Modify();
				LODParentActor->SetIsDirty(true);
			}
		}

		//Make all actors visibile in editor
		SetIsVisibleInEditor(true, true);

		//Reset game standin flag
		UseAsGameStandin(false);

		// Set static mesh to null (this so we can revert without destroying the previously build static mesh)
		//StaticMeshComponent->SetStaticMesh(nullptr);
		// Mark render state dirty to update viewport
		StaticMeshComponent->MarkRenderStateDirty();
	}
}

FName AStandInMeshActor::GenerateBuildKey(bool bRegeneratePipelineHash) const
{
	FString Key = STANDIN_BUILD_KEY_VER;

	//Object Id
	FUniqueObjectGuid ObjectID = FUniqueObjectGuid::GetOrCreateIDForObject(this);
	Key += TEXT("_");
	Key += ObjectID.GetGuid().ToString(EGuidFormats::Digits);
	
	// Initilize pipelie hash
	if (bRegeneratePipelineHash || PipelineHash == 0)
		PipelineHash = GeneratePipelineHash();


	Key += TEXT("_");
	FString PipelineHasString= BytesToHex((uint8*)&PipelineHash, sizeof(uint32));
	Key += PipelineHasString;

	return FName(*Key);
}

void AStandInMeshActor::UpdateBuildKey(bool bRegeneratePipelineHash)
{
	BuildKey = GenerateBuildKey(bRegeneratePipelineHash);
	
}

const bool AStandInMeshActor::HasValidSubActors() const
{
	int32 NumMeshes = 0;

	// Make sure there are at least one meshes in the subactors
	TInlineComponentArray<UStaticMeshComponent*> Components;
	for (AActor* SubActor : SubActors)
	{
		SubActor->GetComponents(/*out*/ Components);
		NumMeshes += Components.Num();

		if (NumMeshes > 0)
		{
			break;
		}
	}

	return NumMeshes > 0;
}

const bool AStandInMeshActor::HasAnySubActors() const
{
	return (SubActors.Num() != 0);
}

void AStandInMeshActor::FinalizeStandInMesh()
{
	UseAsGameStandin(true);
	SetIsVisibleInEditor(true, false);
}

void AStandInMeshActor::SetStaticMesh(class UStaticMesh* InStaticMesh)
{
	if (StaticMeshComponent)
	{
		StaticMeshComponent->SetStaticMesh(InStaticMesh);
		SetIsDirty(false);
		UpdateMemoryInfo();
		if (InStaticMesh != nullptr)
		{
			FinalizeStandInMesh();

			if (StandInGeneratedEvent.IsBound())
			{
				StandInGeneratedEvent.Broadcast();
			}
		}
	}
}

UStaticMesh* AStandInMeshActor::GetStaticMesh() const
{
	return StaticMeshComponent != nullptr ? StaticMeshComponent->GetStaticMesh() : nullptr;
}

void AStandInMeshActor::CleanupInvalidSourceActors()
{

	int NumRemoved = SubActors.RemoveAll([](AActor* InActor) { return InActor == nullptr; });

	if (NumRemoved > 0)
	{
		SetIsDirty(true);
	}
}

void AStandInMeshActor::CleanSubObjectsArray()
{
	/*bool bIsDirty = false;
	for (int32 SubObjectIndex = 0; SubObjectIndex < SubObjects.Num(); ++SubObjectIndex)
	{
		UObject* Object = SubObjects[SubObjectIndex];
		if (Object == nullptr)
		{
			SubObjects.RemoveAtSwap(SubObjectIndex);
			SubObjectIndex--;
			bIsDirty = true;
		}
	}

	if (bIsDirty)
	{
		SetIsDirty(true);
	}*/
}

void AStandInMeshActor::ForceRecache()
{
	//Update Standin triangle count
	TriangleCount = [](UStaticMeshComponent* InStaticMeshComponent)
	{
		if (InStaticMeshComponent == nullptr)
			return 0;

		UStaticMesh* StaticMesh = InStaticMeshComponent->GetStaticMesh();
		if (StaticMesh && StaticMesh->GetRenderData())
		{
			int32 NumTriangles = 0;
			for (int32 SectionIndex = 0; SectionIndex < StaticMesh->GetRenderData()->LODResources[0].Sections.Num(); SectionIndex++)
			{
				const FStaticMeshSection& StaticMeshSection = StaticMesh->GetRenderData()->LODResources[0].Sections[SectionIndex];
				NumTriangles += StaticMeshSection.NumTriangles;
			}

			return NumTriangles;
		}

		return 0;
	}(StaticMeshComponent);

	//Update SubActors total triangle count
	SubActorsTriangleCount = [](TArray<AActor*>& InSubActors)
	{
		int32 NumTriangles = 0;
		TInlineComponentArray<UStaticMeshComponent*> Components;
		for (AActor* SubActor : InSubActors)
		{
			if (SubActor == nullptr)
				continue;

			SubActor->GetComponents(/*out*/ Components);

			for (UStaticMeshComponent* Component : Components)
			{
				UStaticMesh* StaticMesh = Component->GetStaticMesh();
				if (StaticMesh && StaticMesh->GetRenderData())
				{
					for (int32 SectionIndex = 0; SectionIndex < StaticMesh->GetRenderData()->LODResources[0].Sections.Num(); SectionIndex++)
					{
						const FStaticMeshSection& StaticMeshSection = StaticMesh->GetRenderData()->LODResources[0].Sections[SectionIndex];
						NumTriangles += StaticMeshSection.NumTriangles;
					}
				}
			}
		}
		return NumTriangles;
	}(SubActors);
}

int32 AStandInMeshActor::GetNumTriangles() const
{
	return TriangleCount;
}

int32 AStandInMeshActor::GetNumChildrenTriangles() const
{
	return SubActorsTriangleCount;
}

void AStandInMeshActor::UseAsGameStandin(bool bUseAsGameStandin)
{
	for (AActor* SubActor : SubActors)
	{

		HiddenInGame(SubActor, bUseAsGameStandin);
		
	}
	bIsGameStandin = bUseAsGameStandin;
}

void AStandInMeshActor::SetIsVisibleInEditor(bool bStandinVisible, bool bStandinChildrenVisible)
{
	
	for (AActor* SubActor : SubActors)
	{
		TInlineComponentArray<UStaticMeshComponent*> Components;
		SubActor->GetComponents(Components);
		bool bSimplygonUserData = false;
		for (auto& Component : Components)
		{
			USimplygonAssetUserData* SimplygonUserData = Component->GetAssetUserData<USimplygonAssetUserData>();
			if (SimplygonUserData)
			{
				bSimplygonUserData = (SimplygonUserData->MetaTag == ESimplygonMetaTagType::Occluder
					|| SimplygonUserData->MetaTag == ESimplygonMetaTagType::ClippingGeometry
					/*|| SimplygonUserData->MetaTag == ESimplygonMetaTagType::GeometryCameraPath*/);
				break;
			}
		}
		HiddenInEditor(SubActor, !bStandinChildrenVisible && !bSimplygonUserData);
	}
	HiddenInEditor(this, !bStandinVisible);
}

void AStandInMeshActor::HiddenInEditor(bool bHide)
{
	HiddenInEditor(this, bHide);
}

void AStandInMeshActor::HiddenInEditor(AActor* Actor, bool Hide)
{
	Actor->SetIsTemporarilyHiddenInEditor(Hide);
}


uint32 AStandInMeshActor::GeneratePipelineHash() const
{
	TArray<uint8> SettingsAsBytes;
	SettingsAsBytes.Reserve(64);
	SettingsAsBytes.Reset();

	if (Pipeline)
	{
		FPipelineWriter Ar(Pipeline, SettingsAsBytes);
	}

#ifdef DEBUG_PIPELINE_SERIALIZATION
	FFileHelper::SaveArrayToFile(SettingsAsBytes, TEXT("Test.bin"));
#endif
	return FCrc::MemCrc32(SettingsAsBytes.GetData(), SettingsAsBytes.Num());
}

void AStandInMeshActor::HiddenInGame(AActor* Actor, bool Hide)
{
	Actor->bIsEditorOnlyActor = Hide;
}

const int AStandInMeshActor::GetSourceStaticMeshComponentCount() const
{
	int32 TotalMeshes = 0;
	TInlineComponentArray<UStaticMeshComponent*> Components;
	for (const auto& Actor : SubActors)
	{
		Actor->GetComponents<UStaticMeshComponent>(Components, true);
		TotalMeshes += Components.Num();

		for (const auto& Component : Components)
		{
			if (Component->IsA<UInstancedStaticMeshComponent>())
			{
				UInstancedStaticMeshComponent* InstancedComponent = CastChecked<UInstancedStaticMeshComponent>(Component);
				TotalMeshes += (InstancedComponent->GetInstanceCount() - 1);
			}
		}

		TArray<AActor*> AttachedActors;
		Actor->GetAttachedActors(AttachedActors);

		for (const auto& AttachedActor : AttachedActors)
		{
			AttachedActor->GetComponents<UStaticMeshComponent>(Components, true);
			TotalMeshes += Components.Num();

			for (const auto& Component : Components)
			{
				if (Component->IsA<UInstancedStaticMeshComponent>())
				{
					UInstancedStaticMeshComponent* InstancedComponent = CastChecked<UInstancedStaticMeshComponent>(Component);
					TotalMeshes += (InstancedComponent->GetInstanceCount() - 1);
				}
			}
		}

		
	}
	
	return TotalMeshes;
}


void AStandInMeshActor::UpdateMemoryInfo()
{
	MemoryUsedByStandin.Reset();
	MemoryUsedBySourceActors.Reset();

	//NOTE: Estimated Total gives better size estimates.
	// As of writting this not this call only takes into the memory for the standin actor, staticmeshcomponent(excludes static mesh) and group component
	// StaticMesh, Material, Textures are external so need to be accounted for.
	MemoryUsedByStandin.SizeInBytes = float(GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal));
	if (StaticMeshComponent && StaticMeshComponent->GetStaticMesh())
	{
		
		MemoryUsedByStandin.SizeInBytes += GetMemoryUsedByStaticMesh(StaticMeshComponent->GetStaticMesh());
	}


	for (auto& Actor : SubActors)
	{
		MemoryUsedBySourceActors.SizeInBytes = float(Actor->GetResourceSizeBytes(EResourceSizeMode::EstimatedTotal));
		TInlineComponentArray<UStaticMeshComponent*> OutComponents;
		Actor->GetComponents(OutComponents);
		for (auto& Comp : OutComponents)
		{
			MemoryUsedBySourceActors.SizeInBytes += GetMemoryUsedByStaticMesh(Comp->GetStaticMesh());
		}
	}
	
}

#endif // WITH_EDITOR

#undef LOCTEXT_NAMESPACE
